/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package czStvDpds.graphics3D.geometry;

import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector4f;

/**
 * OBB = Orientated Bounding Box (orientovaná kolizní krabice)
 *
 * @author divad
 */
public final class OBB {

    private FloatBuffer original;
    private FloatBuffer transformed;
    private Matrix4f matrix;
    public static int VALUE_PER_VECTOR = 4;

//      5__________8
//    / |         /|
//   /  |        / |
//  1___|_______4  |
//  |   |       |  |
//  |   6_______|__7
//  |  / 	| /
//  | /	        |/
//  2___________3
    /**
     * Nastaví vrcholy OBB dle obrázku:
     *      5__________8
     *    / |         /|
     *   /  |        / |
     *  1___|_______4  |
     *  |   |       |  |
     *  |   6_______|__7
     *  |  / 	    | /
     *  | /	    |/
     *  2___________3
     * 
     * @param vect1
     * @param vect2
     * @param vect3
     * @param vect4
     * @param vect5
     * @param vect6
     * @param vect7
     * @param vect8
     */
    public OBB(Vector4f vect1, Vector4f vect2, Vector4f vect3, Vector4f vect4, Vector4f vect5, Vector4f vect6, Vector4f vect7, Vector4f vect8) {
//        this.original = FloatBuffer.allocate(OBB.VALUE_PER_VECTOR * 8);
        this.original = BufferUtils.createFloatBuffer(OBB.VALUE_PER_VECTOR * 8);
        original.put(vect1.x);
        original.put(vect1.y);
        original.put(vect1.z);
        original.put(vect1.w);

        original.put(vect2.x);
        original.put(vect2.y);
        original.put(vect2.z);
        original.put(vect2.w);

        original.put(vect3.x);
        original.put(vect3.y);
        original.put(vect3.z);
        original.put(vect3.w);

        original.put(vect4.x);
        original.put(vect4.y);
        original.put(vect4.z);
        original.put(vect4.w);

        original.put(vect5.x);
        original.put(vect5.y);
        original.put(vect5.z);
        original.put(vect5.w);

        original.put(vect6.x);
        original.put(vect6.y);
        original.put(vect6.z);
        original.put(vect6.w);

        original.put(vect7.x);
        original.put(vect7.y);
        original.put(vect7.z);
        original.put(vect7.w);

        original.put(vect8.x);
        original.put(vect8.y);
        original.put(vect8.z);
        original.put(vect8.w);

        transformed = original.duplicate();
        original.asReadOnlyBuffer();
    }

    /**
     * Nastaví transformační matici.
     *
     * @param matrix
     */
    public void setMatrix(Matrix4f matrix) {
        this.matrix = matrix;
        if (matrix != null) {
            this.transformData(matrix);
        }
    }

    /**
     * Přepočítá pozice bodů podle transformační matice.
     *
     * @param matrix
     */
    private void transformData(Matrix4f matrix) {
        transformed.rewind();
        for (int i = 0; i < original.capacity() / OBB.VALUE_PER_VECTOR; i++) {
            transformed.put((matrix.m00 * original.get(i * OBB.VALUE_PER_VECTOR) + 0) + (matrix.m10 * original.get(i * OBB.VALUE_PER_VECTOR) + 1) + (matrix.m20 * original.get(i * OBB.VALUE_PER_VECTOR) + 2) + (matrix.m30 * original.get(i * OBB.VALUE_PER_VECTOR) + 3));
            transformed.put((matrix.m01 * original.get(i * OBB.VALUE_PER_VECTOR) + 0) + (matrix.m11 * original.get(i * OBB.VALUE_PER_VECTOR) + 1) + (matrix.m21 * original.get(i * OBB.VALUE_PER_VECTOR) + 2) + (matrix.m31 * original.get(i * OBB.VALUE_PER_VECTOR) + 3));
            transformed.put((matrix.m02 * original.get(i * OBB.VALUE_PER_VECTOR) + 0) + (matrix.m12 * original.get(i * OBB.VALUE_PER_VECTOR) + 1) + (matrix.m22 * original.get(i * OBB.VALUE_PER_VECTOR) + 2) + (matrix.m32 * original.get(i * OBB.VALUE_PER_VECTOR) + 3));
            transformed.put((matrix.m03 * original.get(i * OBB.VALUE_PER_VECTOR) + 0) + (matrix.m13 * original.get(i * OBB.VALUE_PER_VECTOR) + 1) + (matrix.m23 * original.get(i * OBB.VALUE_PER_VECTOR) + 2) + (matrix.m33 * original.get(i * OBB.VALUE_PER_VECTOR) + 3));
        }
    }

    /**
     * Vrátí bydy tak jak byly předány.
     *
     * @return
     */
    public FloatBuffer getOriginal() {
        return original;
    }

    /**
     * Vrátí Buffer s transformovanými body.
     *
     * @return
     */
    public FloatBuffer getTransformed() {
        return transformed;
    }

    /**
     * Převede OBB na AABB - Axis Align Bounding Box (osov+ zarovnaná kolizní
     * krabice).
     *
     * @param min parametr pro uložení levého dolního zadního bodu.
     * @param max parametr pro uložení pravého horního předního bodu.
     */
    public void toAABB(Vector4f min, Vector4f max) {
        if (min == null || max == null) {
            return;
        }
        max.x = this.minOrMax(0, false);
        max.y = this.minOrMax(1, false);
        max.z = this.minOrMax(2, false);
        max.w = this.minOrMax(3, false);

        min.x = this.minOrMax(0, true);
        min.y = this.minOrMax(1, true);
        min.z = this.minOrMax(2, true);
        min.w = this.minOrMax(3, true);
    }

    /**
     * Vrátí nejmenší/největší hodnotu.
     *
     * @param offset
     * @param min
     * @return
     */
    private float minOrMax(int offset, boolean min) {
        float value = transformed.get(offset);
        for (int i = 0; i < transformed.capacity() / OBB.VALUE_PER_VECTOR; i++) {
            float bufferValue = transformed.get(i * OBB.VALUE_PER_VECTOR + offset);
            if (min) {
                value = Math.min(value, bufferValue);
            } else {
                value = Math.max(value, bufferValue);
            }
        }
        return value;
    }

    /**
     * Vrátí transformační matici.
     *
     * @return
     */
    public Matrix4f getMatrix() {
        return matrix;
    }
}
